package com.huifanedu.approval.service.impl;


import cn.hutool.core.util.ObjectUtil;
import com.huifanedu.approval.constant.FlowableConstant;
import com.huifanedu.approval.enums.ApprovalExceptionEnum;
import com.huifanedu.approval.enums.CommentTypeEnum;
import com.huifanedu.approval.factory.model.ApprovalTaskResponse;
import com.huifanedu.approval.listener.TaskListHandler;
import com.huifanedu.approval.model.params.ProcessQueryParam;
import com.huifanedu.approval.model.params.TaskParam;
import com.huifanedu.approval.service.FlowableTaskService;
import com.huifanedu.kernel.core.util.StringUtil;
import com.huifanedu.kernel.model.exception.BusinessException;
import com.huifanedu.kernel.model.pager.PageData;
import com.huifanedu.kernel.model.response.HttpResult;
import com.huifanedu.kernel.redis.entity.CacheUserInfo;
import com.huifanedu.kernel.redis.sercive.CacheService;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.HistoricTaskInstanceQueryProperty;
import org.flowable.task.service.impl.TaskQueryProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <b>功能名：</b>FlowableTaskServiceImpl<br>
 * <b>说明：</b> 流程任务 <br>
 * <b>著作权：</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
 * <b>修改履历：
 *
 * @author 2021-03-10 jiabingbing
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class FlowableTaskServiceImpl extends BaseFlowableServiceImpl implements FlowableTaskService {
    @Autowired
    protected IdentityService identityService;
    @Autowired
    protected RepositoryService repositoryService;
    @Autowired
    protected TaskService taskService;
    @Autowired
    protected RuntimeService runtimeService;
    @Autowired
    protected HistoryService historyService;
    @Autowired
    protected ManagementService managementService;
    @Autowired
    protected FormService formService;
    @Autowired
    private CacheService cacheService;

    @Override
    public HttpResult<PageData<ApprovalTaskResponse>> listTodo(ProcessQueryParam requestParams) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        CacheUserInfo user = cacheService.getCacheUserInfo(request);
        TaskQuery query = createTaskQuery(requestParams);
        query.or().taskAssignee(user.getUsrId()).taskOwner(user.getUsrId()).endOr();
        return this.pageList(requestParams, query, TaskListHandler.class, taskSortMap, TaskQueryProperty.CREATE_TIME);
    }

    @Override
    public HttpResult<PageData<ApprovalTaskResponse>> listDone(ProcessQueryParam requestParams) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        CacheUserInfo user = cacheService.getCacheUserInfo(request);
        HistoricTaskInstanceQuery query = createHistoricTaskInstanceQuery(requestParams);
        query.finished().or().taskAssignee(user.getUsrId()).taskOwner(user.getUsrId()).endOr();
        return this.pageList(requestParams, query, TaskListHandler.class, taskSortMap, HistoricTaskInstanceQueryProperty.TASK_DUE_DATE);
    }

    @Override
    public void completeTask(TaskParam taskRequest) {
        String taskId = taskRequest.getTaskId();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        CacheUserInfo user = cacheService.getCacheUserInfo(request);
        String userId = user.getUsrId();
        Task task = getTaskNotNull(taskId);
        Map<String, Object> completeVariables = taskRequest.getFormMap();
        if (taskRequest.getIsReject() == 1) {
            //nrOfCompletedInstances
            Integer nrOfCompletedInstances = runtimeService.getVariable(task.getExecutionId(), "nrOfCompletedInstances", Integer.class);
            if (nrOfCompletedInstances != null) {
                runtimeService.setVariable(task.getExecutionId(), "nrOfCompletedInstances", nrOfCompletedInstances - 1);
            }
            //添加审批意见
            this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.BH, taskRequest.getMessage());
        } else {
            //添加审批意见
            this.addComment(taskId, task.getProcessInstanceId(), userId, CommentTypeEnum.WC, taskRequest.getMessage());
        }


        if (task.getAssignee() == null || !task.getAssignee().equals(userId)) {
            taskService.setAssignee(taskId, userId);
        }
        //

        taskService.complete(taskId, completeVariables);
    }

    @Override
    public Task getTaskNotNull(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new BusinessException(ApprovalExceptionEnum.APPROVAL_PROCESS_TASK_NOT_FIND);
        }
        return task;
    }

    /**
     * 终止流程
     *
     * @param taskRequest
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stopProcessInstance(TaskParam taskRequest) {
        String taskId = taskRequest.getTaskId();
        Task task = getTaskNotNull(taskId);
        if (StringUtil.isEmpty(taskRequest.getUserId())) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            CacheUserInfo user = cacheService.getCacheUserInfo(request);
            String userId = user.getUsrId();
            taskRequest.setUserId(userId);
        }
        //修改任务变量   为中止
        runtimeService.setVariable(task.getExecutionId(), FlowableConstant.PROCESS_VARIABLE_STATUS, "3");
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        if (bpmnModel != null) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (endNodes != null && endNodes.size() > 0) {
                this.addComment(taskId, processInstance.getProcessInstanceId(), taskRequest.getUserId(), CommentTypeEnum.ZZ, taskRequest.getMessage());
                String endId = endNodes.get(0).getId();
                List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstance.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();
            }
        }
    }


    @Override
    public void addComment(String taskId, String processInstanceId, String userId, CommentTypeEnum type, String message) {
        Authentication.setAuthenticatedUserId(userId);
        type = type == null ? CommentTypeEnum.SP : type;
        message = (message == null || message.length() == 0) ? type.getName() : message;
        taskService.addComment(taskId, processInstanceId, type.toString(), message);
    }

    @Override
    public List<Comment> getComments(String taskId, String processInstanceId, String type, String userId) {
        List<Comment> comments = null;
        if (type == null || type.length() == 0) {
            // 以taskId为优先
            if (taskId != null && taskId.length() > 0) {
                comments = taskService.getTaskComments(taskId);
            } else if (processInstanceId != null && processInstanceId.length() > 0) {
                comments = taskService.getProcessInstanceComments(processInstanceId);
            } else {
                throw new FlowableIllegalArgumentException("taskId processInstanceId type are all empty");
            }
        } else {
            // 以taskId为优先
            if (taskId != null && taskId.length() > 0) {
                comments = taskService.getTaskComments(taskId, type);
            } else if (processInstanceId != null && processInstanceId.length() > 0) {
                comments = taskService.getProcessInstanceComments(processInstanceId, type);
            } else {
                comments = taskService.getCommentsByType(type);
            }
        }
        if (userId != null && userId.length() > 0 && comments != null && comments.size() > 0) {
            comments = comments.stream().filter(comment -> userId.equals(comment.getUserId()))
                    .collect(Collectors.toList());
        }
        return comments;
    }

    protected TaskQuery createTaskQuery(ProcessQueryParam requestParams) {
        TaskQuery query = taskService.createTaskQuery();
        if (StringUtil.isNotEmpty(requestParams.getProcessInstanceId())) {
            query.processInstanceId(requestParams.getProcessInstanceId());
        }
        if (StringUtil.isNotEmpty(requestParams.getProcessInstanceName())) {
            query.processVariableValueLike(FlowableConstant.PROCESS_VARIABLE_APPROVAL_TITLE, "%" + requestParams.getProcessInstanceName() + "%");
        }
        if (StringUtil.isNotEmpty(requestParams.getBuissessKey())) {
            query.processInstanceBusinessKeyLike(requestParams.getBuissessKey());
        }
        if (StringUtil.isNotEmpty(requestParams.getProcessDefId())) {
            query.processDefinitionId(requestParams.getProcessDefId());
        }
        if (ObjectUtil.isNotEmpty(requestParams.getDueAfter())) {
            query.taskDueAfter(requestParams.getDueAfter());
        }
        if (ObjectUtil.isNotEmpty(requestParams.getDueBefore())) {
            query.taskDueBefore(requestParams.getDueBefore());
        }
        if (ObjectUtil.isNotEmpty(requestParams.getCreateBefore())) {
            query.taskCreatedBefore(requestParams.getCreateBefore());
        }
        if (ObjectUtil.isNotEmpty(requestParams.getCreateAfter())) {
            query.taskCreatedAfter(requestParams.getCreateAfter());
        }
        if (FlowableConstant.TASK_EXPIRE_TIME.equals(requestParams.getIsOverTime())) {
            query.taskVariableValueGreaterThan(FlowableConstant.TASK_VARIABLE_OVER_TIME, System.currentTimeMillis());
        }
        if (StringUtil.isNotEmpty(requestParams.getApprovalCatalogId())) {
            query.processVariableValueEquals(FlowableConstant.PROCESS_VARIABLE_APPROVAL_CATALOG_ID, requestParams.getApprovalCatalogId());
        }
        if (StringUtil.isNotEmpty(requestParams.getApprovalInfoId())) {
            query.processVariableValueEquals(FlowableConstant.PROCESS_VARIABLE_APPROVAL_INFO_ID, requestParams.getApprovalInfoId());
        }
        if (StringUtil.isNotEmpty(requestParams.getCcUserId())) {
            query.processVariableValueLike(FlowableConstant.PROCESS_VARIABLE_APPROVAL_CC_USER_IDS_ID, "%" + requestParams.getCcUserId() + "%");
        }
        return query;
    }

    protected HistoricTaskInstanceQuery createHistoricTaskInstanceQuery(ProcessQueryParam requestParams) {
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery();
        if (StringUtil.isNotEmpty(requestParams.getProcessInstanceId())) {
            query.processInstanceId(requestParams.getProcessInstanceId());
        }
        if (StringUtil.isNotEmpty(requestParams.getProcessInstanceName())) {
            query.processVariableValueLike(FlowableConstant.PROCESS_VARIABLE_APPROVAL_TITLE, "%" + requestParams.getProcessInstanceName() + "%");
        }
        if (StringUtil.isNotEmpty(requestParams.getBuissessKey())) {
            query.processInstanceBusinessKeyLike(requestParams.getBuissessKey());
        }
        if (StringUtil.isNotEmpty(requestParams.getProcessDefId())) {
            query.processDefinitionId(requestParams.getProcessDefId());
        }
        if (ObjectUtil.isNotEmpty(requestParams.getDueAfter())) {
            query.taskDueAfter(requestParams.getDueAfter());
        }
        if (ObjectUtil.isNotEmpty(requestParams.getDueBefore())) {
            query.taskDueBefore(requestParams.getDueBefore());
        }
        if (ObjectUtil.isNotEmpty(requestParams.getCreateBefore())) {
            query.taskCreatedBefore(requestParams.getCreateBefore());
        }
        if (ObjectUtil.isNotEmpty(requestParams.getCreateAfter())) {
            query.taskCreatedAfter(requestParams.getCreateAfter());
        }
        if (StringUtil.isNotEmpty(requestParams.getApprovalCatalogId())) {
            query.processVariableValueEquals(FlowableConstant.PROCESS_VARIABLE_APPROVAL_CATALOG_ID, requestParams.getApprovalCatalogId());
        }
        if (StringUtil.isNotEmpty(requestParams.getApprovalInfoId())) {
            query.processVariableValueEquals(FlowableConstant.PROCESS_VARIABLE_APPROVAL_INFO_ID, requestParams.getApprovalInfoId());
        }
        if (StringUtil.isNotEmpty(requestParams.getCcUserId())) {
            query.processVariableValueLike(FlowableConstant.PROCESS_VARIABLE_APPROVAL_CC_USER_IDS_ID, "%" + requestParams.getCcUserId() + "%");
        }
        if (StringUtil.isNotEmpty(requestParams.getIsCcToMeReaded())) {
            if (FlowableConstant.PROCESS_VARIABLE_APPROVAL_CC_READ.equals(requestParams.getIsCcToMeReaded())) {
                query.processVariableValueEquals(requestParams.getCcUserId(), FlowableConstant.PROCESS_VARIABLE_APPROVAL_CC_READ);
            } else {
                query.processVariableValueEquals(requestParams.getCcUserId(), FlowableConstant.PROCESS_VARIABLE_APPROVAL_CC_NO_READ);
            }
        }
        return query;
    }
}